// -*- C++ -*-
//===--------------------------- tuple ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCUDACXX_TUPLE
#define _LIBCUDACXX_TUPLE

/*
    tuple synopsis

namespace std
{

template <class... T>
class tuple {
public:
    explicit(see-below) constexpr tuple();
    explicit(see-below) tuple(const T&...);  // constexpr in C++14
    template <class... U>
        explicit(see-below) tuple(U&&...);  // constexpr in C++14
    tuple(const tuple&) = default;
    tuple(tuple&&) = default;
    template <class... U>
        explicit(see-below) tuple(const tuple<U...>&);  // constexpr in C++14
    template <class... U>
        explicit(see-below) tuple(tuple<U...>&&);  // constexpr in C++14
    template <class U1, class U2>
        explicit(see-below) tuple(const pair<U1, U2>&); // iff sizeof...(T) == 2
// constexpr in C++14 template <class U1, class U2> explicit(see-below)
tuple(pair<U1, U2>&&); // iff sizeof...(T) == 2  // constexpr in C++14

    // allocator-extended constructors
    template <class Alloc>
        tuple(allocator_arg_t, const Alloc& a);
    template <class Alloc>
        explicit(see-below) tuple(allocator_arg_t, const Alloc& a, const T&...);
    template <class Alloc, class... U>
        explicit(see-below) tuple(allocator_arg_t, const Alloc& a, U&&...);
    template <class Alloc>
        tuple(allocator_arg_t, const Alloc& a, const tuple&);
    template <class Alloc>
        tuple(allocator_arg_t, const Alloc& a, tuple&&);
    template <class Alloc, class... U>
        explicit(see-below) tuple(allocator_arg_t, const Alloc& a, const
tuple<U...>&); template <class Alloc, class... U> explicit(see-below)
tuple(allocator_arg_t, const Alloc& a, tuple<U...>&&); template <class Alloc,
class U1, class U2> explicit(see-below) tuple(allocator_arg_t, const Alloc& a,
const pair<U1, U2>&); template <class Alloc, class U1, class U2>
        explicit(see-below) tuple(allocator_arg_t, const Alloc& a, pair<U1,
U2>&&);

    tuple& operator=(const tuple&);
    tuple&
        operator=(tuple&&) noexcept(AND(is_nothrow_move_assignable<T>::value
...)); template <class... U> tuple& operator=(const tuple<U...>&); template
<class... U> tuple& operator=(tuple<U...>&&); template <class U1, class U2>
        tuple& operator=(const pair<U1, U2>&); // iff sizeof...(T) == 2
    template <class U1, class U2>
        tuple& operator=(pair<U1, U2>&&); // iff sizeof...(T) == 2

    void swap(tuple&) noexcept(AND(swap(declval<T&>(), declval<T&>())...));
};

template <class ...T>
tuple(T...) -> tuple<T...>;                                         // since
C++17 template <class T1, class T2> tuple(pair<T1, T2>) -> tuple<T1, T2>; //
since C++17 template <class Alloc, class ...T> tuple(allocator_arg_t, Alloc,
T...) -> tuple<T...>;                 // since C++17 template <class Alloc,
class T1, class T2> tuple(allocator_arg_t, Alloc, pair<T1, T2>) -> tuple<T1,
T2>;       // since C++17 template <class Alloc, class ...T>
tuple(allocator_arg_t, Alloc, tuple<T...>) -> tuple<T...>;          // since
C++17

inline constexpr unspecified ignore;

template <class... T> tuple<V...>  make_tuple(T&&...); // constexpr in C++14
template <class... T> tuple<ATypes...> forward_as_tuple(T&&...) noexcept; //
constexpr in C++14 template <class... T> tuple<T&...> tie(T&...) noexcept; //
constexpr in C++14 template <class... Tuples> tuple<CTypes...>
tuple_cat(Tuples&&... tpls); // constexpr in C++14

// [tuple.apply], calling a function with a tuple of arguments:
template <class F, class Tuple>
  constexpr decltype(auto) apply(F&& f, Tuple&& t); // C++17
template <class T, class Tuple>
  constexpr T make_from_tuple(Tuple&& t); // C++17

// 20.4.1.4, tuple helper classes:
template <class T> struct tuple_size; // undefined
template <class... T> struct tuple_size<tuple<T...>>;
template <class T>
 inline constexpr size_t tuple_size_v = tuple_size<T>::value; // C++17
template <size_t I, class T> struct tuple_element; // undefined
template <size_t I, class... T> struct tuple_element<I, tuple<T...>>;
template <size_t I, class T>
  using tuple_element_t = typename tuple_element <I, T>::type; // C++14

// 20.4.1.5, element access:
template <size_t I, class... T>
    typename tuple_element<I, tuple<T...>>::type&
    get(tuple<T...>&) noexcept; // constexpr in C++14
template <size_t I, class... T>
    const typename tuple_element<I, tuple<T...>>::type&
    get(const tuple<T...>&) noexcept; // constexpr in C++14
template <size_t I, class... T>
    typename tuple_element<I, tuple<T...>>::type&&
    get(tuple<T...>&&) noexcept; // constexpr in C++14
template <size_t I, class... T>
    const typename tuple_element<I, tuple<T...>>::type&&
    get(const tuple<T...>&&) noexcept; // constexpr in C++14

template <class T1, class... T>
    constexpr T1& get(tuple<T...>&) noexcept;  // C++14
template <class T1, class... T>
    constexpr const T1& get(const tuple<T...>&) noexcept;   // C++14
template <class T1, class... T>
    constexpr T1&& get(tuple<T...>&&) noexcept;   // C++14
template <class T1, class... T>
    constexpr const T1&& get(const tuple<T...>&&) noexcept;   // C++14

// 20.4.1.6, relational operators:
template<class... T, class... U> bool operator==(const tuple<T...>&, const
tuple<U...>&); // constexpr in C++14 template<class... T, class... U> bool
operator<(const tuple<T...>&, const tuple<U...>&);  // constexpr in C++14
template<class... T, class... U> bool operator!=(const tuple<T...>&, const
tuple<U...>&); // constexpr in C++14 template<class... T, class... U> bool
operator>(const tuple<T...>&, const tuple<U...>&);  // constexpr in C++14
template<class... T, class... U> bool operator<=(const tuple<T...>&, const
tuple<U...>&); // constexpr in C++14 template<class... T, class... U> bool
operator>=(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14

template <class... Types, class Alloc>
  struct uses_allocator<tuple<Types...>, Alloc>;

template <class... Types>
  void
  swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(noexcept(x.swap(y)));

}  // std

*/

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
#  pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
#  pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
#  pragma system_header
#endif // no system header

#include <cuda/std/__functional/unwrap_ref.h>
#include <cuda/std/__fwd/array.h>
#include <cuda/std/__memory/allocator_arg_t.h>
#include <cuda/std/__tuple_dir/ignore.h>
#include <cuda/std/__tuple_dir/make_tuple_types.h>
#include <cuda/std/__tuple_dir/sfinae_helpers.h>
#include <cuda/std/__tuple_dir/structured_bindings.h>
#include <cuda/std/__tuple_dir/tuple_element.h>
#include <cuda/std/__tuple_dir/tuple_indices.h>
#include <cuda/std/__tuple_dir/tuple_like.h>
#include <cuda/std/__tuple_dir/tuple_size.h>
#include <cuda/std/__tuple_dir/tuple_types.h>
#include <cuda/std/__tuple_dir/vector_types.h>
#include <cuda/std/__type_traits/copy_cvref.h>
#include <cuda/std/__type_traits/maybe_const.h>
#include <cuda/std/__type_traits/reference_constructs_from_temporary.h>
#include <cuda/std/__type_traits/remove_const.h>
#include <cuda/std/__utility/forward.h>
#include <cuda/std/__utility/integer_sequence.h>
#include <cuda/std/__utility/move.h>
#include <cuda/std/__utility/pair.h>
#include <cuda/std/__utility/piecewise_construct.h>
#include <cuda/std/__utility/swap.h>
#include <cuda/std/climits>
#include <cuda/std/cstddef>
#include <cuda/std/cstdint>
#include <cuda/std/type_traits>
#include <cuda/std/utility>

// standard-mandated includes
#include <cuda/std/version>

// [tuple.syn]
#if _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()
#  include <cuda/std/compare>
#endif // _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()

#include <cuda/std/__cccl/prologue.h>

_CCCL_BEGIN_NAMESPACE_CUDA_STD

template <class>
struct __is_tuple_of_iterator_references : ::cuda::std::false_type
{};

// __tuple_leaf
struct __tuple_leaf_default_constructor_tag
{};

enum class __tuple_leaf_specialization
{
  __default,
  __synthesize_assignment,
  __empty_non_final,
};

template <class _Tp>
_CCCL_API constexpr __tuple_leaf_specialization __tuple_leaf_choose()
{
  return is_empty_v<_Tp> && !is_final_v<_Tp> ? __tuple_leaf_specialization::__empty_non_final
       : __must_synthesize_assignment_v<_Tp>
         ? __tuple_leaf_specialization::__synthesize_assignment
         : __tuple_leaf_specialization::__default;
}

template <size_t _Ip, class _Hp, __tuple_leaf_specialization = __tuple_leaf_choose<_Hp>()>
class __tuple_leaf;

_CCCL_EXEC_CHECK_DISABLE
template <size_t _Ip, class _Hp, __tuple_leaf_specialization _Ep>
_CCCL_API inline void swap(__tuple_leaf<_Ip, _Hp, _Ep>& __x,
                           __tuple_leaf<_Ip, _Hp, _Ep>& __y) noexcept(is_nothrow_swappable_v<_Hp>)
{
  swap(__x.get(), __y.get());
}

_CCCL_DIAG_PUSH
_CCCL_DIAG_SUPPRESS_MSVC(4244) // conversion from '_Tp' to '_Hp', possible loss of data

template <size_t _Ip, class _Hp>
class __tuple_leaf<_Ip, _Hp, __tuple_leaf_specialization::__default>
{
  _Hp __value_;

#if defined(_CCCL_BUILTIN_REFERENCE_CONSTRUCTS_FROM_TEMPORARY)
  template <class _Up>
  static constexpr bool __can_bind_reference = !reference_constructs_from_temporary_v<_Hp&, _Up>;
#else
  template <class _Up>
  static constexpr bool __can_bind_reference = true;
#endif // !_CCCL_BUILTIN_REFERENCE_CONSTRUCTS_FROM_TEMPORARY

public:
  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API constexpr __tuple_leaf() noexcept(is_nothrow_default_constructible_v<_Hp>)
      : __value_()
  {}

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API constexpr __tuple_leaf(__tuple_leaf_default_constructor_tag) noexcept(
    is_nothrow_default_constructible_v<_Hp>)
      : __value_()
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Alloc>
  _CCCL_API inline __tuple_leaf(integral_constant<int, 0>, const _Alloc&)
      : __value_()
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Alloc>
  _CCCL_API inline __tuple_leaf(integral_constant<int, 1>, const _Alloc& __a)
      : __value_(allocator_arg_t(), __a)
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Alloc>
  _CCCL_API inline __tuple_leaf(integral_constant<int, 2>, const _Alloc& __a)
      : __value_(__a)
  {}

  template <class _Tp>
  using __can_forward = _And<_IsNotSame<remove_cvref_t<_Tp>, __tuple_leaf>, is_constructible<_Hp, _Tp>>;

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, enable_if_t<__can_forward<_Tp>::value, int> = 0>
  _CCCL_API constexpr explicit __tuple_leaf(_Tp&& __t) noexcept(is_nothrow_constructible_v<_Hp, _Tp>)
      : __value_(::cuda::std::forward<_Tp>(__t))
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Alloc>
  _CCCL_API inline explicit __tuple_leaf(integral_constant<int, 0>, const _Alloc&, _Tp&& __t)
      : __value_(::cuda::std::forward<_Tp>(__t))
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Alloc>
  _CCCL_API inline explicit __tuple_leaf(integral_constant<int, 1>, const _Alloc& __a, _Tp&& __t)
      : __value_(allocator_arg_t(), __a, ::cuda::std::forward<_Tp>(__t))
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Alloc>
  _CCCL_API inline explicit __tuple_leaf(integral_constant<int, 2>, const _Alloc& __a, _Tp&& __t)
      : __value_(::cuda::std::forward<_Tp>(__t), __a)
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp>
  _CCCL_API inline __tuple_leaf& operator=(_Tp&& __t) noexcept(is_nothrow_assignable_v<_Hp&, _Tp>)
  {
    __value_ = ::cuda::std::forward<_Tp>(__t);
    return *this;
  }

  _CCCL_API inline int swap(__tuple_leaf& __t) noexcept(is_nothrow_swappable_v<__tuple_leaf>)
  {
    ::cuda::std::swap(*this, __t);
    return 0;
  }

  _CCCL_API constexpr _Hp& get() noexcept
  {
    return __value_;
  }
  _CCCL_API constexpr const _Hp& get() const noexcept
  {
    return __value_;
  }
};

template <size_t _Ip, class _Hp>
class __tuple_leaf<_Ip, _Hp, __tuple_leaf_specialization::__synthesize_assignment>
{
  _Hp __value_;

#if defined(_CCCL_BUILTIN_REFERENCE_CONSTRUCTS_FROM_TEMPORARY)
  template <class _Up>
  static constexpr bool __can_bind_reference = !reference_constructs_from_temporary_v<_Hp&, _Up>;
#else
  template <class _Up>
  static constexpr bool __can_bind_reference = true;
#endif // !_CCCL_BUILTIN_REFERENCE_CONSTRUCTS_FROM_TEMPORARY

public:
  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API constexpr __tuple_leaf() noexcept(is_nothrow_default_constructible_v<_Hp>)
      : __value_()
  {
    static_assert(!is_reference_v<_Hp>, "Attempted to default construct a reference element in a tuple");
  }

  template <class _Tp>
  using __can_forward = _And<_IsNotSame<remove_cvref_t<_Tp>, __tuple_leaf>, is_constructible<_Hp, _Tp>>;

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, enable_if_t<__can_forward<_Tp>::value, int> = 0>
  _CCCL_API constexpr explicit __tuple_leaf(_Tp&& __t) noexcept(is_nothrow_constructible_v<_Hp, _Tp>)
      : __value_(::cuda::std::forward<_Tp>(__t))
  {
    static_assert(__can_bind_reference<_Tp&&>,
                  "Attempted construction of reference element "
                  "binds to a temporary whose lifetime has ended");
  }

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Alloc>
  _CCCL_API inline explicit __tuple_leaf(integral_constant<int, 0>, const _Alloc&, _Tp&& __t)
      : __value_(::cuda::std::forward<_Tp>(__t))
  {
    static_assert(__can_bind_reference<_Tp&&>,
                  "Attempted construction of reference element binds to a "
                  "temporary whose lifetime has ended");
  }

  _CCCL_EXEC_CHECK_DISABLE
  __tuple_leaf(const __tuple_leaf& __t) = default;
  _CCCL_EXEC_CHECK_DISABLE
  __tuple_leaf(__tuple_leaf&& __t) = default;

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API constexpr __tuple_leaf& operator=(const __tuple_leaf& __t) noexcept
  {
    __value_ = __t.__value_;
    return *this;
  }
  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API constexpr __tuple_leaf& operator=(__tuple_leaf&& __t) noexcept
  {
    __value_ = ::cuda::std::move(__t.__value_);
    return *this;
  }

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp>
  _CCCL_API inline __tuple_leaf& operator=(_Tp&& __t) noexcept(is_nothrow_assignable_v<_Hp&, _Tp>)
  {
    __value_ = ::cuda::std::forward<_Tp>(__t);
    return *this;
  }

  _CCCL_API inline int swap(__tuple_leaf& __t) noexcept(is_nothrow_swappable_v<__tuple_leaf>)
  {
    ::cuda::std::swap(*this, __t);
    return 0;
  }

  _CCCL_API constexpr _Hp& get() noexcept
  {
    return __value_;
  }
  _CCCL_API constexpr const _Hp& get() const noexcept
  {
    return __value_;
  }
};

template <size_t _Ip, class _Hp>
class __tuple_leaf<_Ip, _Hp, __tuple_leaf_specialization::__empty_non_final> : private remove_const_t<_Hp>
{
public:
  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API constexpr __tuple_leaf() noexcept(is_nothrow_default_constructible<_Hp>::value) {}

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API constexpr __tuple_leaf(__tuple_leaf_default_constructor_tag) noexcept(
    is_nothrow_default_constructible_v<_Hp>)
      : _Hp()
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Alloc>
  _CCCL_API inline __tuple_leaf(integral_constant<int, 0>, const _Alloc&)
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Alloc>
  _CCCL_API inline __tuple_leaf(integral_constant<int, 1>, const _Alloc& __a)
      : _Hp(allocator_arg_t(), __a)
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Alloc>
  _CCCL_API inline __tuple_leaf(integral_constant<int, 2>, const _Alloc& __a)
      : _Hp(__a)
  {}

  template <class _Tp>
  using __can_forward = _And<_IsNotSame<remove_cvref_t<_Tp>, __tuple_leaf>, is_constructible<_Hp, _Tp>>;

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, enable_if_t<__can_forward<_Tp>::value, int> = 0>
  _CCCL_API constexpr explicit __tuple_leaf(_Tp&& __t) noexcept((is_nothrow_constructible<_Hp, _Tp>::value))
      : _Hp(::cuda::std::forward<_Tp>(__t))
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Alloc>
  _CCCL_API inline explicit __tuple_leaf(integral_constant<int, 0>, const _Alloc&, _Tp&& __t)
      : _Hp(::cuda::std::forward<_Tp>(__t))
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Alloc>
  _CCCL_API inline explicit __tuple_leaf(integral_constant<int, 1>, const _Alloc& __a, _Tp&& __t)
      : _Hp(allocator_arg_t(), __a, ::cuda::std::forward<_Tp>(__t))
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Alloc>
  _CCCL_API inline explicit __tuple_leaf(integral_constant<int, 2>, const _Alloc& __a, _Tp&& __t)
      : _Hp(::cuda::std::forward<_Tp>(__t), __a)
  {}

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, enable_if_t<is_assignable_v<_Hp&, const _Tp&>, int> = 0>
  _CCCL_API inline __tuple_leaf& operator=(const _Tp& __t) noexcept(is_nothrow_assignable_v<_Hp&, const _Tp&>)
  {
    _Hp::operator=(__t);
    return *this;
  }

  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, enable_if_t<is_assignable_v<_Hp&, _Tp>, int> = 0>
  _CCCL_API inline __tuple_leaf& operator=(_Tp&& __t) noexcept(is_nothrow_assignable_v<_Hp&, _Tp>)
  {
    _Hp::operator=(::cuda::std::forward<_Tp>(__t));
    return *this;
  }

  _CCCL_EXEC_CHECK_DISABLE
  _CCCL_API inline int swap(__tuple_leaf& __t) noexcept(is_nothrow_swappable_v<__tuple_leaf>)
  {
    ::cuda::std::swap(*this, __t);
    return 0;
  }

  _CCCL_API constexpr _Hp& get() noexcept
  {
    return static_cast<_Hp&>(*this);
  }
  _CCCL_API constexpr const _Hp& get() const noexcept
  {
    return static_cast<const _Hp&>(*this);
  }
};

_CCCL_DIAG_POP

template <class... _Tp>
_CCCL_API inline void __swallow(_Tp&&...) noexcept
{}

template <class _Tp>
struct __all_default_constructible;

template <class... _Tp>
struct __all_default_constructible<__tuple_types<_Tp...>> : __all<is_default_constructible_v<_Tp>...>
{};

struct __tuple_variadic_constructor_tag
{};

// __tuple_impl

template <class... _Tp>
inline constexpr bool __tuple_all_copy_assignable_v = (is_copy_assignable_v<_Tp> && ... && true);

template <class... _Tp>
inline constexpr bool __tuple_all_move_assignable_v = (is_move_assignable_v<_Tp> && ... && true);

template <class _Indx, class... _Tp>
struct _CCCL_DECLSPEC_EMPTY_BASES __tuple_impl;

template <size_t... _Indx, class... _Tp>
struct _CCCL_DECLSPEC_EMPTY_BASES __tuple_impl<__tuple_indices<_Indx...>, _Tp...>
    : public __tuple_leaf<_Indx, _Tp>...
    , public __tuple_impl_sfinae_helper<__tuple_impl<__tuple_indices<_Indx...>, _Tp...>,
                                        __tuple_all_copy_assignable_v<_Tp...>,
                                        __tuple_all_move_assignable_v<_Tp...>>
{
  _CCCL_API constexpr __tuple_impl() noexcept(__all<is_nothrow_default_constructible_v<_Tp>...>::value)
      : __tuple_leaf<_Indx, _Tp>()...
  {}

  // Handle non-allocator, full initialization
  // Old MSVC cannot handle the noexept specifier outside of template arguments
  template <class... _Up,
            enable_if_t<sizeof...(_Up) == sizeof...(_Tp), int> = 0,
            bool __all_nothrow_constructible                   = __all<is_nothrow_constructible_v<_Tp, _Up>...>::value>
  _CCCL_API constexpr explicit __tuple_impl(__tuple_variadic_constructor_tag,
                                            _Up&&... __u) noexcept(__all_nothrow_constructible)
      : __tuple_leaf<_Indx, _Tp>(::cuda::std::forward<_Up>(__u))...
  {}

  // Handle non-allocator, partial default initialization
  // Recursively delegate until we have full rank
  template <class... _Up, enable_if_t<sizeof...(_Up) < sizeof...(_Tp), int> = 0>
  _CCCL_API constexpr explicit __tuple_impl(__tuple_variadic_constructor_tag __tag, _Up&&... __u) noexcept(
    noexcept(__tuple_impl(__tag, ::cuda::std::forward<_Up>(__u)..., __tuple_leaf_default_constructor_tag{})))
      : __tuple_impl(__tag, ::cuda::std::forward<_Up>(__u)..., __tuple_leaf_default_constructor_tag{})
  {}

  // Handle allocator aware, full initialization
  template <class _Alloc, class... _Up, enable_if_t<sizeof...(_Up) == sizeof...(_Tp), int> = 0>
  _CCCL_API inline explicit __tuple_impl(
    allocator_arg_t, const _Alloc& __a, __tuple_variadic_constructor_tag, _Up&&... __u)
      : __tuple_leaf<_Indx, _Tp>(__uses_alloc_ctor<_Tp, _Alloc, _Up>(), __a, ::cuda::std::forward<_Up>(__u))...
  {}

  // Handle allocator aware, full default initialization
  template <class _Alloc>
  _CCCL_API inline explicit __tuple_impl(allocator_arg_t, const _Alloc& __a)
      : __tuple_leaf<_Indx, _Tp>(__uses_alloc_ctor<_Tp, _Alloc>(), __a)...
  {}

  template <class _Tuple, size_t _Indx2>
  using __tuple_elem_at = tuple_element_t<_Indx2, __make_tuple_types_t<_Tuple>>;

  template <class _Tuple, enable_if_t<__tuple_constructible<_Tuple, tuple<_Tp...>>::value, int> = 0>
  _CCCL_API constexpr __tuple_impl(_Tuple&& __t) noexcept(
    (__all<is_nothrow_constructible_v<_Tp, __tuple_elem_at<_Tuple, _Indx>>...>::value))
      : __tuple_leaf<_Indx, _Tp>(::cuda::std::forward<__tuple_elem_at<_Tuple, _Indx>>(::cuda::std::get<_Indx>(__t)))...
  {}

  template <class _Alloc, class _Tuple, enable_if_t<__tuple_constructible<_Tuple, tuple<_Tp...>>::value, int> = 0>
  _CCCL_API inline __tuple_impl(allocator_arg_t, const _Alloc& __a, _Tuple&& __t)
      : __tuple_leaf<_Indx, _Tp>(__uses_alloc_ctor<_Tp, _Alloc, __tuple_elem_at<_Tuple, _Indx>>(),
                                 __a,
                                 ::cuda::std::forward<__tuple_elem_at<_Tuple, _Indx>>(::cuda::std::get<_Indx>(__t)))...
  {}

  template <class _Tuple, enable_if_t<__tuple_assignable<_Tuple, tuple<_Tp...>>::value, int> = 0>
  _CCCL_API inline __tuple_impl&
  operator=(_Tuple&& __t) noexcept((__all<is_nothrow_assignable_v<_Tp&, __tuple_elem_at<_Tuple, _Indx>>...>::value))
  {
    ::cuda::std::__swallow(__tuple_leaf<_Indx, _Tp>::operator=(
      ::cuda::std::forward<__tuple_elem_at<_Tuple, _Indx>>(::cuda::std::get<_Indx>(__t)))...);
    return *this;
  }

  _CCCL_HIDE_FROM_ABI __tuple_impl(const __tuple_impl&)            = default;
  _CCCL_HIDE_FROM_ABI __tuple_impl(__tuple_impl&&)                 = default;
  _CCCL_HIDE_FROM_ABI __tuple_impl& operator=(const __tuple_impl&) = default;
  _CCCL_HIDE_FROM_ABI __tuple_impl& operator=(__tuple_impl&&)      = default;

  _CCCL_API inline void swap(__tuple_impl& __t) noexcept(__all<is_nothrow_swappable_v<_Tp>...>::value)
  {
    ::cuda::std::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t))...);
  }
};

struct __invalid_tuple_constraints
{
  static constexpr bool __implicit_constructible = false;
  static constexpr bool __explicit_constructible = false;
  static constexpr bool __nothrow_constructible  = false;
};

template <class... _Tp>
struct __tuple_constraints
{
  static constexpr bool __implicit_default_constructible =
    __all<__is_implicitly_default_constructible<_Tp>::value...>::value;

  static constexpr bool __explicit_default_constructible =
    !__implicit_default_constructible && __all<is_default_constructible_v<_Tp>...>::value;

  static constexpr bool __nothrow_default_constructible = __all<is_nothrow_default_constructible_v<_Tp>...>::value;

  static constexpr bool __implicit_variadic_copy_constructible =
    __tuple_constructible<tuple<const _Tp&...>, tuple<_Tp...>>::value
    && __tuple_convertible<tuple<const _Tp&...>, tuple<_Tp...>>::value;

  static constexpr bool __explicit_variadic_copy_constructible =
    __tuple_constructible<tuple<const _Tp&...>, tuple<_Tp...>>::value
    && !__tuple_convertible<tuple<const _Tp&...>, tuple<_Tp...>>::value;

  static constexpr bool __nothrow_variadic_copy_constructible = __all<is_nothrow_copy_constructible_v<_Tp>...>::value;

  template <class... _Args>
  struct _PackExpandsToThisTuple : false_type
  {};

  template <class _Arg>
  struct _PackExpandsToThisTuple<_Arg> : is_same<remove_cvref_t<_Arg>, tuple<_Tp...>>
  {};

  template <class... _Args>
  struct __variadic_constraints
  {
    static constexpr bool __implicit_constructible =
      __tuple_constructible<tuple<_Args...>, tuple<_Tp...>>::value
      && __tuple_convertible<tuple<_Args...>, tuple<_Tp...>>::value;

    static constexpr bool __explicit_constructible =
      __tuple_constructible<tuple<_Args...>, tuple<_Tp...>>::value
      && !__tuple_convertible<tuple<_Args...>, tuple<_Tp...>>::value;

    static constexpr bool __nothrow_constructible = __all<is_nothrow_constructible_v<_Tp, _Args>...>::value;
  };

  template <class... _Args>
  struct __variadic_constraints_less_rank
  {
    static constexpr bool __implicit_constructible =
      __tuple_constructible<tuple<_Args...>, __make_tuple_types_t<tuple<_Tp...>, sizeof...(_Args)>>::value
      && __tuple_convertible<tuple<_Args...>, __make_tuple_types_t<tuple<_Tp...>, sizeof...(_Args)>>::value
      && __all_default_constructible<__make_tuple_types_t<tuple<_Tp...>, sizeof...(_Tp), sizeof...(_Args)>>::value;

    static constexpr bool __explicit_constructible =
      __tuple_constructible<tuple<_Args...>, __make_tuple_types_t<tuple<_Tp...>, sizeof...(_Args)>>::value
      && !__tuple_convertible<tuple<_Args...>, __make_tuple_types_t<tuple<_Tp...>, sizeof...(_Args)>>::value
      && __all_default_constructible<__make_tuple_types_t<tuple<_Tp...>, sizeof...(_Tp), sizeof...(_Args)>>::value;
  };

  template <class _Tuple>
  struct __valid_tuple_like_constraints
  {
    static constexpr bool __implicit_constructible =
      __tuple_constructible<_Tuple, tuple<_Tp...>>::value && __tuple_convertible<_Tuple, tuple<_Tp...>>::value;

    static constexpr bool __explicit_constructible =
      __tuple_constructible<_Tuple, tuple<_Tp...>>::value && !__tuple_convertible<_Tuple, tuple<_Tp...>>::value;
  };

  template <class _Tuple>
  struct __valid_tuple_like_constraints_rank_one
  {
    template <class _Tuple2>
    struct _PreferTupleLikeConstructorImpl
        : _Or<
            // Don't attempt the two checks below if the tuple we are given
            // has the same type as this tuple.
            _IsSame<remove_cvref_t<_Tuple2>, tuple<_Tp...>>,
            _Lazy<_And, _Not<is_constructible<_Tp..., _Tuple2>>, _Not<is_convertible<_Tuple2, _Tp...>>>>
    {};

    // This trait is used to disable the tuple-like constructor when
    // the UTypes... constructor should be selected instead.
    // See LWG issue #2549.
    template <class _Tuple2>
    using _PreferTupleLikeConstructor = _PreferTupleLikeConstructorImpl<_Tuple2>;

    static constexpr bool __implicit_constructible =
      __tuple_constructible<_Tuple, tuple<_Tp...>>::value && __tuple_convertible<_Tuple, tuple<_Tp...>>::value
      && _PreferTupleLikeConstructor<_Tuple>::value;

    static constexpr bool __explicit_constructible =
      __tuple_constructible<_Tuple, tuple<_Tp...>>::value && !__tuple_convertible<_Tuple, tuple<_Tp...>>::value
      && _PreferTupleLikeConstructor<_Tuple>::value;
  };

  template <class _Tuple>
  using __tuple_like_constraints =
    _If<sizeof...(_Tp) == 1, __valid_tuple_like_constraints_rank_one<_Tuple>, __valid_tuple_like_constraints<_Tuple>>;
};

template <class... _Tp>
class _CCCL_TYPE_VISIBILITY_DEFAULT tuple
{
  using _BaseT = __tuple_impl<__make_tuple_indices_t<sizeof...(_Tp)>, _Tp...>;

  _BaseT __base_;

  template <class... _Args>
  struct _PackExpandsToThisTuple : false_type
  {};

  template <class _Arg>
  struct _PackExpandsToThisTuple<_Arg> : is_same<remove_cvref_t<_Arg>, tuple>
  {};

public:
  template <size_t _Ip>
  _CCCL_API constexpr tuple_element_t<_Ip, tuple>& __get_impl() & noexcept
  {
    using type _CCCL_NODEBUG_ALIAS = tuple_element_t<_Ip, tuple>;
    return static_cast<__tuple_leaf<_Ip, type>&>(__base_).get();
  }

  template <size_t _Ip>
  _CCCL_API constexpr const tuple_element_t<_Ip, tuple>& __get_impl() const& noexcept
  {
    using type _CCCL_NODEBUG_ALIAS = tuple_element_t<_Ip, tuple>;
    return static_cast<const __tuple_leaf<_Ip, type>&>(__base_).get();
  }

  template <size_t _Ip>
  _CCCL_API constexpr tuple_element_t<_Ip, tuple>&& __get_impl() && noexcept
  {
    using type _CCCL_NODEBUG_ALIAS = tuple_element_t<_Ip, tuple>;
    return static_cast<type&&>(static_cast<__tuple_leaf<_Ip, type>&&>(__base_).get());
  }

  template <size_t _Ip>
  _CCCL_API constexpr const tuple_element_t<_Ip, tuple>&& __get_impl() const&& noexcept
  {
    using type _CCCL_NODEBUG_ALIAS = tuple_element_t<_Ip, tuple>;
    return static_cast<const type&&>(static_cast<const __tuple_leaf<_Ip, type>&&>(__base_).get());
  }

  template <class _Constraints                                               = __tuple_constraints<_Tp...>,
            enable_if_t<_Constraints::__implicit_default_constructible, int> = 0>
  _CCCL_API constexpr tuple() noexcept(_Constraints::__nothrow_default_constructible)
  {}

  template <class _Constraints                                               = __tuple_constraints<_Tp...>,
            enable_if_t<_Constraints::__explicit_default_constructible, int> = 0>
  _CCCL_API explicit constexpr tuple() noexcept(_Constraints::__nothrow_default_constructible)
  {}

  _CCCL_HIDE_FROM_ABI tuple(tuple const&) = default;
  _CCCL_HIDE_FROM_ABI tuple(tuple&&)      = default;

  template <class _AllocArgT,
            class _Alloc,
            class _Constraints                                               = __tuple_constraints<_Tp...>,
            enable_if_t<is_same_v<allocator_arg_t, _AllocArgT>, int>         = 0,
            enable_if_t<_Constraints::__implicit_default_constructible, int> = 0>
  _CCCL_API inline tuple(_AllocArgT, _Alloc const& __a) noexcept(_Constraints::__nothrow_default_constructible)
      : __base_(allocator_arg_t(), __a)
  {}

  template <class _AllocArgT,
            class _Alloc,
            class _Constraints                                               = __tuple_constraints<_Tp...>,
            enable_if_t<is_same_v<allocator_arg_t, _AllocArgT>, int>         = 0,
            enable_if_t<_Constraints::__explicit_default_constructible, int> = 0>
  explicit _CCCL_API inline tuple(_AllocArgT, _Alloc const& __a) noexcept(_Constraints::__nothrow_default_constructible)
      : __base_(allocator_arg_t(), __a)
  {}

  template <class _Constraints                                                     = __tuple_constraints<_Tp...>,
            enable_if_t<_Constraints::__implicit_variadic_copy_constructible, int> = 0>
  _CCCL_API constexpr tuple(const _Tp&... __t) noexcept(_Constraints::__nothrow_variadic_copy_constructible)
      : __base_(__tuple_variadic_constructor_tag{}, __t...)
  {}

  template <class _Constraints                                                     = __tuple_constraints<_Tp...>,
            enable_if_t<_Constraints::__explicit_variadic_copy_constructible, int> = 0>
  _CCCL_API constexpr explicit tuple(const _Tp&... __t) noexcept(_Constraints::__nothrow_variadic_copy_constructible)
      : __base_(__tuple_variadic_constructor_tag{}, __t...)
  {}

  template <class _Alloc,
            class _Constraints                                                     = __tuple_constraints<_Tp...>,
            enable_if_t<_Constraints::__implicit_variadic_copy_constructible, int> = 0>
  _CCCL_API inline tuple(allocator_arg_t, const _Alloc& __a, const _Tp&... __t) noexcept(
    _Constraints::__nothrow_variadic_copy_constructible)
      : __base_(allocator_arg_t(), __a, __tuple_variadic_constructor_tag{}, __t...)
  {}

  template <class _Alloc,
            class _Constraints                                                     = __tuple_constraints<_Tp...>,
            enable_if_t<_Constraints::__explicit_variadic_copy_constructible, int> = 0>
  _CCCL_API inline explicit tuple(allocator_arg_t, const _Alloc& __a, const _Tp&... __t) noexcept(
    _Constraints::__nothrow_variadic_copy_constructible)
      : __base_(allocator_arg_t(), __a, __tuple_variadic_constructor_tag{}, __t...)
  {}

  template <class... _Up>
  using __variadic_constraints =
    _If<!_PackExpandsToThisTuple<_Up...>::value && sizeof...(_Up) == sizeof...(_Tp),
        typename __tuple_constraints<_Tp...>::template __variadic_constraints<_Up...>,
        __invalid_tuple_constraints>;

  template <class... _Up,
            class _Constraints                                       = __variadic_constraints<_Up...>,
            enable_if_t<_Constraints::__implicit_constructible, int> = 0>
  _CCCL_API constexpr tuple(_Up&&... __u) noexcept(_Constraints::__nothrow_constructible)
      : __base_(__tuple_variadic_constructor_tag{}, ::cuda::std::forward<_Up>(__u)...)
  {}

  template <class... _Up,
            class _Constraints                                       = __variadic_constraints<_Up...>,
            enable_if_t<_Constraints::__explicit_constructible, int> = 0>
  _CCCL_API constexpr explicit tuple(_Up&&... __u) noexcept(_Constraints::__nothrow_constructible)
      : __base_(__tuple_variadic_constructor_tag{}, ::cuda::std::forward<_Up>(__u)...)
  {}

  template <class... _Up>
  using __variadic_constraints_less_rank =
    _If<!_PackExpandsToThisTuple<_Up...>::value,
        typename __tuple_constraints<_Tp...>::template __variadic_constraints_less_rank<_Up...>,
        __invalid_tuple_constraints>;

  template <class... _Up,
            class _Constraints                                       = __variadic_constraints_less_rank<_Up...>,
            enable_if_t<sizeof...(_Up) < sizeof...(_Tp), int>        = 0,
            enable_if_t<_Constraints::__implicit_constructible, int> = 0>
  _CCCL_API constexpr explicit tuple(_Up&&... __u) noexcept(is_nothrow_constructible_v<_BaseT, _Up...>)
      : __base_(__tuple_variadic_constructor_tag{}, ::cuda::std::forward<_Up>(__u)...)
  {}

  template <class _Alloc,
            class... _Up,
            class _Constraints                                       = __variadic_constraints<_Up...>,
            enable_if_t<_Constraints::__implicit_constructible, int> = 0>
  _CCCL_API inline tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u) noexcept(
    _Constraints::__nothrow_constructible)
      : __base_(allocator_arg_t(), __a, __tuple_variadic_constructor_tag{}, ::cuda::std::forward<_Up>(__u)...)
  {}

  template <class _Alloc,
            class... _Up,
            class _Constraints                                       = __variadic_constraints<_Up...>,
            enable_if_t<_Constraints::__explicit_constructible, int> = 0>
  _CCCL_API inline explicit tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u) noexcept(
    _Constraints::__nothrow_constructible)
      : __base_(allocator_arg_t(), __a, __tuple_variadic_constructor_tag{}, ::cuda::std::forward<_Up>(__u)...)
  {}

  template <class _Tuple>
  using __tuple_like_constraints =
    _If<__tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value,
        typename __tuple_constraints<_Tp...>::template __tuple_like_constraints<_Tuple>,
        __invalid_tuple_constraints>;

  // Horrible hack to make tuple_of_iterator_references work
  template <class _TupleOfIteratorReferences,
            enable_if_t<__is_tuple_of_iterator_references<_TupleOfIteratorReferences>::value, int> = 0,
            enable_if_t<(tuple_size<_TupleOfIteratorReferences>::value == sizeof...(_Tp)), int>    = 0>
  _CCCL_API constexpr tuple(_TupleOfIteratorReferences&& __t)
      : tuple(::cuda::std::forward<_TupleOfIteratorReferences>(__t),
              typename __make_tuple_indices<sizeof...(_Tp)>::type{})
  {}

private:
  template <class _TupleOfIteratorReferences,
            size_t... _Indices,
            enable_if_t<__is_tuple_of_iterator_references<_TupleOfIteratorReferences>::value, int> = 0>
  _CCCL_API constexpr tuple(_TupleOfIteratorReferences&& __t, __tuple_indices<_Indices...>)
      : tuple(::cuda::std::get<_Indices>(::cuda::std::forward<_TupleOfIteratorReferences>(__t))...)
  {}

public:
  template <class _Tuple,
            class _Constraints                                        = __tuple_like_constraints<_Tuple>,
            enable_if_t<!_PackExpandsToThisTuple<_Tuple>::value, int> = 0,
            enable_if_t<!is_lvalue_reference_v<_Tuple>, int>          = 0,
            enable_if_t<_Constraints::__implicit_constructible, int>  = 0>
  _CCCL_API constexpr tuple(_Tuple&& __t) noexcept(is_nothrow_constructible_v<_BaseT, _Tuple>)
      : __base_(::cuda::std::forward<_Tuple>(__t))
  {}

  template <class _Tuple,
            class _Constraints                                        = __tuple_like_constraints<const _Tuple&>,
            enable_if_t<!_PackExpandsToThisTuple<_Tuple>::value, int> = 0,
            enable_if_t<_Constraints::__implicit_constructible, int>  = 0>
  _CCCL_API constexpr tuple(const _Tuple& __t) noexcept(is_nothrow_constructible_v<_BaseT, const _Tuple&>)
      : __base_(__t)
  {}

  template <class _Tuple,
            class _Constraints                                        = __tuple_like_constraints<_Tuple>,
            enable_if_t<!_PackExpandsToThisTuple<_Tuple>::value, int> = 0,
            enable_if_t<!is_lvalue_reference_v<_Tuple>, int>          = 0,
            enable_if_t<_Constraints::__explicit_constructible, int>  = 0>
  _CCCL_API constexpr explicit tuple(_Tuple&& __t) noexcept(is_nothrow_constructible_v<_BaseT, _Tuple>)
      : __base_(::cuda::std::forward<_Tuple>(__t))
  {}

  template <class _Tuple,
            class _Constraints                                        = __tuple_like_constraints<const _Tuple&>,
            enable_if_t<!_PackExpandsToThisTuple<_Tuple>::value, int> = 0,
            enable_if_t<_Constraints::__explicit_constructible, int>  = 0>
  _CCCL_API constexpr explicit tuple(const _Tuple& __t) noexcept(is_nothrow_constructible_v<_BaseT, const _Tuple&>)
      : __base_(__t)
  {}

  template <class _Alloc,
            class _Tuple,
            class _Constraints                                       = __tuple_like_constraints<_Tuple>,
            enable_if_t<_Constraints::__implicit_constructible, int> = 0>
  _CCCL_API inline tuple(allocator_arg_t, const _Alloc& __a, _Tuple&& __t)
      : __base_(allocator_arg_t(), __a, ::cuda::std::forward<_Tuple>(__t))
  {}

  template <class _Alloc,
            class _Tuple,
            class _Constraints                                       = __tuple_like_constraints<_Tuple>,
            enable_if_t<_Constraints::__explicit_constructible, int> = 0>
  _CCCL_API inline explicit tuple(allocator_arg_t, const _Alloc& __a, _Tuple&& __t)
      : __base_(allocator_arg_t(), __a, ::cuda::std::forward<_Tuple>(__t))
  {}

  using _CanCopyAssign = __all<is_copy_assignable_v<_Tp>...>;
  using _CanMoveAssign = __all<is_move_assignable_v<_Tp>...>;

  _CCCL_HIDE_FROM_ABI tuple& operator=(const tuple& __t) = default;
  _CCCL_HIDE_FROM_ABI tuple& operator=(tuple&& __t)      = default;

  template <class _Tuple, enable_if_t<__tuple_assignable<_Tuple, tuple>::value, bool> = false>
  _CCCL_API inline tuple& operator=(_Tuple&& __t) noexcept(is_nothrow_assignable_v<_BaseT&, _Tuple>)
  {
    __base_.operator=(::cuda::std::forward<_Tuple>(__t));
    return *this;
  }

  _CCCL_API inline void swap(tuple& __t) noexcept(__all<is_nothrow_swappable_v<_Tp>...>::value)
  {
    __base_.swap(__t.__base_);
  }
};

template <>
class _CCCL_TYPE_VISIBILITY_DEFAULT tuple<>
{
public:
  _CCCL_HIDE_FROM_ABI constexpr tuple() noexcept = default;
  template <class _Alloc>
  _CCCL_API inline tuple(allocator_arg_t, const _Alloc&) noexcept
  {}
  template <class _Alloc>
  _CCCL_API inline tuple(allocator_arg_t, const _Alloc&, const tuple&) noexcept
  {}
  template <class _Up>
  _CCCL_API inline tuple(array<_Up, 0>) noexcept
  {}
  template <class _Alloc, class _Up>
  _CCCL_API inline tuple(allocator_arg_t, const _Alloc&, array<_Up, 0>) noexcept
  {}
  _CCCL_API inline void swap(tuple&) noexcept {}
};

template <class... _Tp>
_CCCL_HOST_DEVICE tuple(_Tp...) -> tuple<_Tp...>;
template <class _Tp1, class _Tp2>
_CCCL_HOST_DEVICE tuple(pair<_Tp1, _Tp2>) -> tuple<_Tp1, _Tp2>;
template <class _Alloc, class... _Tp>
_CCCL_HOST_DEVICE tuple(allocator_arg_t, _Alloc, _Tp...) -> tuple<_Tp...>;
template <class _Alloc, class _Tp1, class _Tp2>
_CCCL_HOST_DEVICE tuple(allocator_arg_t, _Alloc, pair<_Tp1, _Tp2>) -> tuple<_Tp1, _Tp2>;
template <class _Alloc, class... _Tp>
_CCCL_HOST_DEVICE tuple(allocator_arg_t, _Alloc, tuple<_Tp...>) -> tuple<_Tp...>;

template <class... _Tp>
_CCCL_API inline enable_if_t<__all<is_nothrow_swappable_v<_Tp>...>::value, void>
swap(tuple<_Tp...>& __t, tuple<_Tp...>& __u) noexcept(__all<is_nothrow_swappable_v<_Tp>...>::value)
{
  __t.swap(__u);
}

// get
template <size_t _Ip, class... _Tp>
_CCCL_API constexpr tuple_element_t<_Ip, tuple<_Tp...>>& get(tuple<_Tp...>& __t) noexcept
{
  return __t.template __get_impl<_Ip>();
}

template <size_t _Ip, class... _Tp>
_CCCL_API constexpr const tuple_element_t<_Ip, tuple<_Tp...>>& get(const tuple<_Tp...>& __t) noexcept
{
  return __t.template __get_impl<_Ip>();
}

template <size_t _Ip, class... _Tp>
_CCCL_API constexpr tuple_element_t<_Ip, tuple<_Tp...>>&& get(tuple<_Tp...>&& __t) noexcept
{
  return ::cuda::std::move(__t).template __get_impl<_Ip>();
}

template <size_t _Ip, class... _Tp>
_CCCL_API constexpr const tuple_element_t<_Ip, tuple<_Tp...>>&& get(const tuple<_Tp...>&& __t) noexcept
{
  return ::cuda::std::move(__t).template __get_impl<_Ip>();
}

namespace __find_detail
{

static constexpr size_t __not_found = ~size_t(0);
static constexpr size_t __ambiguous = __not_found - 1;

_CCCL_API constexpr size_t __find_idx_return(size_t __curr_i, size_t __res, bool __matches)
{
  return !__matches ? __res : (__res == __not_found ? __curr_i : __ambiguous);
}

template <size_t _Nx>
_CCCL_API constexpr size_t __find_idx(size_t __i, const bool (&__matches)[_Nx])
{
  return __i == _Nx ? __not_found : __find_idx_return(__i, __find_idx(__i + 1, __matches), __matches[__i]);
}

template <class _T1, class... _Args>
struct __find_exactly_one_checked
{
  static constexpr bool __matches[sizeof...(_Args)] = {is_same_v<_T1, _Args>...};
  static constexpr size_t value                     = __find_detail::__find_idx(0, __matches);
  static_assert(value != __not_found, "type not found in type list");
  static_assert(value != __ambiguous, "type occurs more than once in type list");
};

template <class _T1>
struct __find_exactly_one_checked<_T1>
{
  static_assert(!is_same_v<_T1, _T1>, "type not in empty type list");
};

} // namespace __find_detail

template <typename _T1, typename... _Args>
struct __find_exactly_one_t : public __find_detail::__find_exactly_one_checked<_T1, _Args...>
{};

template <class _T1, class... _Args>
_CCCL_API constexpr _T1& get(tuple<_Args...>& __tup) noexcept
{
  return ::cuda::std::get<__find_exactly_one_t<_T1, _Args...>::value>(__tup);
}

template <class _T1, class... _Args>
_CCCL_API constexpr _T1 const& get(tuple<_Args...> const& __tup) noexcept
{
  return ::cuda::std::get<__find_exactly_one_t<_T1, _Args...>::value>(__tup);
}

template <class _T1, class... _Args>
_CCCL_API constexpr _T1&& get(tuple<_Args...>&& __tup) noexcept
{
  return ::cuda::std::get<__find_exactly_one_t<_T1, _Args...>::value>(::cuda::std::move(__tup));
}

template <class _T1, class... _Args>
_CCCL_API constexpr _T1 const&& get(tuple<_Args...> const&& __tup) noexcept
{
  return ::cuda::std::get<__find_exactly_one_t<_T1, _Args...>::value>(::cuda::std::move(__tup));
}

// tie

template <class... _Tp>
_CCCL_API constexpr tuple<_Tp&...> tie(_Tp&... __t) noexcept
{
  return tuple<_Tp&...>(__t...);
}

template <class... _Tp>
_CCCL_API constexpr tuple<unwrap_ref_decay_t<_Tp>...> make_tuple(_Tp&&... __t)
{
  return tuple<unwrap_ref_decay_t<_Tp>...>(::cuda::std::forward<_Tp>(__t)...);
}

template <class... _Tp>
_CCCL_API constexpr tuple<_Tp&&...> forward_as_tuple(_Tp&&... __t) noexcept
{
  return tuple<_Tp&&...>(::cuda::std::forward<_Tp>(__t)...);
}

template <size_t _Ip>
struct __tuple_equal
{
  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Up>
  _CCCL_API constexpr bool operator()(const _Tp& __x, const _Up& __y)
  {
    return __tuple_equal<_Ip - 1>()(__x, __y) && ::cuda::std::get<_Ip - 1>(__x) == ::cuda::std::get<_Ip - 1>(__y);
  }
};

template <>
struct __tuple_equal<0>
{
  template <class _Tp, class _Up>
  _CCCL_API constexpr bool operator()(const _Tp&, const _Up&)
  {
    return true;
  }
};

template <class... _Tp, class... _Up>
_CCCL_API constexpr bool operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
{
  static_assert(sizeof...(_Tp) == sizeof...(_Up), "Can't compare tuples of different sizes");
  return __tuple_equal<sizeof...(_Tp)>()(__x, __y);
}

template <class... _Tp, class... _Up>
_CCCL_API constexpr bool operator!=(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
{
  return !(__x == __y);
}

template <size_t _Ip>
struct __tuple_less
{
  _CCCL_EXEC_CHECK_DISABLE
  template <class _Tp, class _Up>
  _CCCL_API constexpr bool operator()(const _Tp& __x, const _Up& __y)
  {
    const size_t __idx = tuple_size<_Tp>::value - _Ip;
    if (::cuda::std::get<__idx>(__x) < ::cuda::std::get<__idx>(__y))
    {
      return true;
    }
    if (::cuda::std::get<__idx>(__y) < ::cuda::std::get<__idx>(__x))
    {
      return false;
    }
    return __tuple_less<_Ip - 1>()(__x, __y);
  }
};

template <>
struct __tuple_less<0>
{
  template <class _Tp, class _Up>
  _CCCL_API constexpr bool operator()(const _Tp&, const _Up&)
  {
    return false;
  }
};

template <class... _Tp, class... _Up>
_CCCL_API constexpr bool operator<(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
{
  static_assert(sizeof...(_Tp) == sizeof...(_Up), "Can't compare tuples of different sizes");
  return __tuple_less<sizeof...(_Tp)>()(__x, __y);
}

template <class... _Tp, class... _Up>
_CCCL_API constexpr bool operator>(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
{
  return __y < __x;
}

template <class... _Tp, class... _Up>
_CCCL_API constexpr bool operator>=(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
{
  return !(__x < __y);
}

template <class... _Tp, class... _Up>
_CCCL_API constexpr bool operator<=(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
{
  return !(__y < __x);
}

// tuple_cat

template <class _Tp, class _Up>
struct __tuple_cat_type;

template <class... _Ttypes, class... _Utypes>
struct __tuple_cat_type<tuple<_Ttypes...>, __tuple_types<_Utypes...>>
{
  using type _CCCL_NODEBUG_ALIAS = tuple<_Ttypes..., _Utypes...>;
};

template <class _ResultTuple, bool _Is_Tuple0TupleLike, class... _Tuples>
struct __tuple_cat_return_1
{};

template <class... _Types, class _Tuple0>
struct __tuple_cat_return_1<tuple<_Types...>, true, _Tuple0>
{
  using type _CCCL_NODEBUG_ALIAS =
    typename __tuple_cat_type<tuple<_Types...>, __make_tuple_types_t<remove_cvref_t<_Tuple0>>>::type;
};

template <class... _Types, class _Tuple0, class _Tuple1, class... _Tuples>
struct __tuple_cat_return_1<tuple<_Types...>, true, _Tuple0, _Tuple1, _Tuples...>
    : public __tuple_cat_return_1<
        typename __tuple_cat_type<tuple<_Types...>, __make_tuple_types_t<remove_cvref_t<_Tuple0>>>::type,
        __tuple_like_impl<remove_reference_t<_Tuple1>>,
        _Tuple1,
        _Tuples...>
{};

template <class... _Tuples>
struct __tuple_cat_return;

template <class _Tuple0, class... _Tuples>
struct __tuple_cat_return<_Tuple0, _Tuples...>
    : public __tuple_cat_return_1<tuple<>, __tuple_like_impl<remove_reference_t<_Tuple0>>, _Tuple0, _Tuples...>
{};

template <>
struct __tuple_cat_return<>
{
  using type _CCCL_NODEBUG_ALIAS = tuple<>;
};

_CCCL_API constexpr tuple<> tuple_cat()
{
  return tuple<>();
}

template <class _Rp, class _Indices, class _Tuple0, class... _Tuples>
struct __tuple_cat_return_ref_imp;

template <class... _Types, size_t... _I0, class _Tuple0>
struct __tuple_cat_return_ref_imp<tuple<_Types...>, __tuple_indices<_I0...>, _Tuple0>
{
  using _T0 _CCCL_NODEBUG_ALIAS = remove_reference_t<_Tuple0>;
  using type                    = tuple<_Types..., __copy_cvref_t<_Tuple0, tuple_element_t<_I0, _T0>>&&...>;
};

template <class... _Types, size_t... _I0, class _Tuple0, class _Tuple1, class... _Tuples>
struct __tuple_cat_return_ref_imp<tuple<_Types...>, __tuple_indices<_I0...>, _Tuple0, _Tuple1, _Tuples...>
    : public __tuple_cat_return_ref_imp<
        tuple<_Types..., __copy_cvref_t<_Tuple0, tuple_element_t<_I0, remove_reference_t<_Tuple0>>>&&...>,
        __make_tuple_indices_t<tuple_size<remove_reference_t<_Tuple1>>::value>,
        _Tuple1,
        _Tuples...>
{};

template <class _Tuple0, class... _Tuples>
struct __tuple_cat_return_ref
    : public __tuple_cat_return_ref_imp<tuple<>,
                                        __make_tuple_indices_t<tuple_size<remove_reference_t<_Tuple0>>::value>,
                                        _Tuple0,
                                        _Tuples...>
{};

template <class _Types, class _I0, class _J0>
struct __tuple_cat;

template <class... _Types, size_t... _I0, size_t... _J0>
struct __tuple_cat<tuple<_Types...>, __tuple_indices<_I0...>, __tuple_indices<_J0...>>
{
  template <class _Tuple0>
  _CCCL_API constexpr typename __tuple_cat_return_ref<tuple<_Types...>&&, _Tuple0&&>::type
  operator()([[maybe_unused]] tuple<_Types...> __t, _Tuple0&& __t0)
  {
    return ::cuda::std::forward_as_tuple(::cuda::std::forward<_Types>(::cuda::std::get<_I0>(__t))...,
                                         ::cuda::std::get<_J0>(::cuda::std::forward<_Tuple0>(__t0))...);
  }

  template <class _Tuple0, class _Tuple1, class... _Tuples>
  _CCCL_API constexpr typename __tuple_cat_return_ref<tuple<_Types...>&&, _Tuple0&&, _Tuple1&&, _Tuples&&...>::type
  operator()([[maybe_unused]] tuple<_Types...> __t, _Tuple0&& __t0, _Tuple1&& __t1, _Tuples&&... __tpls)
  {
    using _T0 _CCCL_NODEBUG_ALIAS = remove_reference_t<_Tuple0>;
    using _T1 _CCCL_NODEBUG_ALIAS = remove_reference_t<_Tuple1>;
    return __tuple_cat<tuple<_Types..., __copy_cvref_t<_Tuple0, tuple_element_t<_J0, _T0>>&&...>,
                       __make_tuple_indices_t<sizeof...(_Types) + tuple_size<_T0>::value>,
                       __make_tuple_indices_t<tuple_size<_T1>::value>>()(
      ::cuda::std::forward_as_tuple(::cuda::std::forward<_Types>(::cuda::std::get<_I0>(__t))...,
                                    ::cuda::std::get<_J0>(::cuda::std::forward<_Tuple0>(__t0))...),
      ::cuda::std::forward<_Tuple1>(__t1),
      ::cuda::std::forward<_Tuples>(__tpls)...);
  }
};

template <class _Tuple0, class... _Tuples>
_CCCL_API constexpr typename __tuple_cat_return<_Tuple0, _Tuples...>::type tuple_cat(_Tuple0&& __t0, _Tuples&&... __tpls)
{
  using _T0 _CCCL_NODEBUG_ALIAS = remove_reference_t<_Tuple0>;
  return __tuple_cat<tuple<>, __tuple_indices<>, __make_tuple_indices_t<tuple_size<_T0>::value>>()(
    tuple<>(), ::cuda::std::forward<_Tuple0>(__t0), ::cuda::std::forward<_Tuples>(__tpls)...);
}

template <class... _Tp, class _Alloc>
struct _CCCL_TYPE_VISIBILITY_DEFAULT uses_allocator<tuple<_Tp...>, _Alloc> : true_type
{};

template <class _T1, class _T2, bool _IsRef>
template <class... _Args1, class... _Args2, size_t... _I1, size_t... _I2>
_CCCL_API inline _CCCL_CONSTEXPR_CXX20 __pair_base<_T1, _T2, _IsRef>::__pair_base(
  piecewise_construct_t,
  tuple<_Args1...>& __first_args,
  tuple<_Args2...>& __second_args,
  __tuple_indices<_I1...>,
  __tuple_indices<_I2...>)
    : first(::cuda::std::forward<_Args1>(::cuda::std::get<_I1>(__first_args))...)
    , second(::cuda::std::forward<_Args2>(::cuda::std::get<_I2>(__second_args))...)
{}

#define _LIBCUDACXX_NOEXCEPT_RETURN(...) \
  noexcept(noexcept(__VA_ARGS__))        \
  {                                      \
    return __VA_ARGS__;                  \
  }

template <class _Fn, class _Tuple, size_t... _Id>
_CCCL_API constexpr decltype(auto) __apply_tuple_impl(_Fn&& __f, _Tuple&& __t, __tuple_indices<_Id...>)
  _LIBCUDACXX_NOEXCEPT_RETURN(
    ::cuda::std::__invoke(::cuda::std::forward<_Fn>(__f), ::cuda::std::get<_Id>(::cuda::std::forward<_Tuple>(__t))...))

    template <class _Fn, class _Tuple>
    _CCCL_API constexpr decltype(auto) apply(_Fn&& __f, _Tuple&& __t)
      _LIBCUDACXX_NOEXCEPT_RETURN(::cuda::std::__apply_tuple_impl(
        ::cuda::std::forward<_Fn>(__f),
        ::cuda::std::forward<_Tuple>(__t),
        __make_tuple_indices_t<tuple_size_v<remove_reference_t<_Tuple>>>{}))

        template <class _Tp, class _Tuple, size_t... _Idx>
        _CCCL_API constexpr _Tp
  __make_from_tuple_impl(_Tuple&& __t, __tuple_indices<_Idx...>)
    _LIBCUDACXX_NOEXCEPT_RETURN(_Tp(::cuda::std::get<_Idx>(::cuda::std::forward<_Tuple>(__t))...))

      template <class _Tp, class _Tuple>
      _CCCL_API constexpr _Tp
  make_from_tuple(_Tuple&& __t) _LIBCUDACXX_NOEXCEPT_RETURN(::cuda::std::__make_from_tuple_impl<_Tp>(
    ::cuda::std::forward<_Tuple>(__t), __make_tuple_indices_t<tuple_size_v<remove_reference_t<_Tuple>>>{}))

#undef _LIBCUDACXX_NOEXCEPT_RETURN

    _CCCL_END_NAMESPACE_CUDA_STD

#include <cuda/std/__cccl/epilogue.h>

#endif // _LIBCUDACXX_TUPLE
